package com.gxa.hualianeshop.jmslistener;

import com.gxa.hualianeshop.common.base.RedisKeys;
import lombok.extern.slf4j.Slf4j;
import org.apache.activemq.command.ActiveMQTextMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.jms.config.SimpleJmsListenerContainerFactory;
import org.springframework.stereotype.Component;

import javax.jms.*;

/**
 * @author bill
 * @date 2023/7/13 15:13
 */
@Slf4j
@Component
public class GoodsCacheListener {

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;

    /**
     * 消费消息的方法
     * 1. 当服务器启动的时候,立刻执行监听,此时队列中有消息,就立刻接收(消费)
     * 2. 服务器运行过程中,监听器一直持续不断的监听
     *
     * queue模式
     * 生产1条消息, 存在两个监听消费者,它们采用轮流接收
     *
     * topic模式
     * 生产1条消息, 存在两个监听消费者,它们同时接收
     * 测试用例上运行的时候,启动测试用例就执行了一次, 执行的时候又发送一次, 会有两次出列
     *
     * 如何确保消息一定被发送
     * 如何确保消息一定被消费?
     *
     * 如何使用事务模式和签收模式?
     * 如果要增强可靠性:  1.修改事务模式为false
     *                  2. 签收模式(Active) 设为4 , ack()
     *  从发送角度:
     *  JmsTemplate -> ConnectionFactory(指定事务和签收)
     *  通过事务保障消息被发出
     *
     *  从接收角度:
     *  containerFactory容器容器工厂
     *

     */

    @Bean
    public SimpleJmsListenerContainerFactory firstFactory(ConnectionFactory connectionFactory) {
        SimpleJmsListenerContainerFactory factory=new SimpleJmsListenerContainerFactory();
        //关闭事务: 开启手动事务
        factory.setSessionTransacted(false);
        // 开启手动签收
        factory.setSessionAcknowledgeMode(4);
        factory.setConnectionFactory(connectionFactory);
        return factory;
    }

    /**
     * containerFactory: 表示目标容器工厂的bean的name
     * 通过声明SimpleJmsListenerContainerFactory来修改事务模式和签收模式
     *
     * 消息的签收过程(可靠性)
     * 需要Message 对象
     * Message: jms设定的消息统一接口
     * TextMessage: 处理文本类型的Message, getText()
     * ObjectMessage: 处理对象类型的Message  getObject()
     *
     * ActiveMQTextMessage: ActiveMQ实现的TextMessage接口的实现类,
     * ActiveMQObjectMessage: ActiveMQ实现的ObjectMessage接口的实现类
     *
     * 当传递字符串类型("1")的消息时,会自动封装为TextMessage / ActiveMQTextMessage
     * 当传递其它类型(1)的消息时,会自动封装为ObjectMessage / ActiveMQObjectMessage
     *
     *
     * @param session mq的会话对象(控制事务和ack)
     * @param message  Message对象
     * @throws JMSException
     */
    @JmsListener(destination = "goodsCacheId",containerFactory = "firstFactory")
    public void receive1(Session session, Message message) throws JMSException {

        //获取当前的事务状态
        boolean transacted = session.getTransacted();

        int acknowledgeMode = session.getAcknowledgeMode();

        log.info("transacted事务状态:{}",transacted);
        log.info("acknowledgeMode签收模式:{}",acknowledgeMode);

        // 获取消息的id
        String jmsMessageID = message.getJMSMessageID();


        // Message -> TextMessage ->ActiveMQTextMessage
        String spuId =  ((TextMessage)message).getText();
        log.info("从goodsCacheId队列中监听到的消息:{}",spuId);



        // 监听到要删除的目标spu的id
        // 通过redis执行删除操作
        try{
        String key = RedisKeys.GOODS_KEY+spuId;
        Boolean delete = false;

        // 先创建线程池执行 删除业务
            // 通过component模块添加线程池的子模块


        // 自旋锁:
            // 先通过exists判断是否存在
            // delete的boolean变量的状态为false
            // 进入while循环 , 通过redis操作改变delete状态
            // 如果删除成功, delete变成true,跳出循环 ,结束操作
            while(delete!=null && !delete){
                // 增加一个休眠时间
                Thread.sleep(10);
                delete = redisTemplate.delete(key);
            }


        //消息的签收方法(ACK)

            message.acknowledge();
        }catch (Exception e){
            throw new RuntimeException(e);
        }



    }


}
